import tkinter as tk
from tkinter import simpledialog
from tkinter import filedialog   
from tkinter import ttk

import json
import csv 

# 定义一个接受json文件路径和字典的类。
# 在该类中可实现将字典数据保存到接送文件中，
# 以及从接送文件中加载字典数据。
class JsonDictHandler:
    def __init__(self, json_file_path):
        self.json_file_path = json_file_path
    
    def save_dict(self, data_dict):
        # 保存字典数据到json文件中，使用try-except捕获异常,utf-8编码
        try:
            with open(self.json_file_path, "w", encoding="utf-8") as json_file:
                json.dump(data_dict, json_file, indent=4, ensure_ascii=False)
        except Exception as e:
            print(f"保存字典数据到json文件中时出错: {e}")
        return None
    
    def load_dict(self):
        # 从json文件中加载字典数据，使用try-except捕获异常,utf-8编码
        try:
            with open(self.json_file_path, "r", encoding="utf-8") as json_file:
                data_dict = json.load(json_file)
        except Exception as e:
            print(f"从json文件中加载字典数据时出错: {e}")
            return None 
        return data_dict



# 定义左右分开的容器
class SeparatedFrame(tk.Frame):
    
    def __init__(self, parent, side, padx, pady, left=True):
        super().__init__(parent)
        self.pack(side=side, padx=padx, pady=pady)
        
        # 左边容器需要一个列表框和两个按钮
        if left:
            self.stats_listbox = tk.Listbox(self)
            self.stats_listbox.pack(pady=10)
        
            # 两个按钮加入到frame中 
            self.button_frame = tk.Frame(self)
            self.button_frame.pack(pady=30)
            
            self.add_button = tk.Button(self.button_frame, text="添加统计类")
            self.add_button.pack(side=tk.LEFT, padx=5)
            
            self.remove_button = tk.Button(self.button_frame, text="移除统计类")
            self.remove_button.pack(side=tk.LEFT, padx=5)
            
            self.save_button = tk.Button(self.button_frame, text="保存统计类")
            self.save_button.pack(side=tk.LEFT, padx=5)
            
            # 按钮用于导出数据
            self.export_button = tk.Button(self.button_frame, text="导出数据")
            self.export_button.pack(side=tk.LEFT, padx=5)
            
        else:
            # 右边容器需要一个树状视图和四个按钮
            self.stats_treeview = ttk.Treeview(self)
            self.stats_treeview.pack(pady=10)
            
            # 向树状图添加标题，具体项和数目，居中对齐
            self.stats_treeview["columns"] = ("item", "count")
            self.stats_treeview.heading("item", text="具体项", anchor="w")
            self.stats_treeview.heading("count", text="数目", anchor="w")
            self.stats_treeview["show"] = "headings"
            
            # 树状图列宽
            self.stats_treeview.column("item", width=150)
            self.stats_treeview.column("count", width=100)
        
            # 四个按钮加入到frame中 
            self.button_frame = tk.Frame(self)
            self.button_frame.pack(pady=10)
            
            self.add_button = tk.Button(self.button_frame, text="添加具体项")
            self.add_button.pack(side=tk.LEFT, padx=5)
            
            self.remove_button = tk.Button(self.button_frame, text="移除具体项")
            self.remove_button.pack(side=tk.LEFT, padx=5)
            
            # 用于加1的按钮
            self.increment_button = tk.Button(self.button_frame, text="+1")
            self.increment_button.pack(side=tk.LEFT, padx=5)
            
            # 用于减1的按钮
            self.decrement_button = tk.Button(self.button_frame, text="-1")
            self.decrement_button.pack(side=tk.LEFT, padx=5)

class SimpleStatsGUI(tk.Tk):
    # 定义map，关键字为字符串，值为列表，列表元素为[具体项，数目]
    stats_map = {}
    # 定义json文件路径
    json_file_path = "stats_map.json"
    # 定义json文件处理类
    json_dict_handler = JsonDictHandler(json_file_path)
    
    def __init__(self):
        super().__init__()
        self.title("简单统计GUI")
        self.geometry("600x400")
        
        # 左右容器
        self.left_frame = SeparatedFrame(self, "left", 10, 10, left=True)
        self.right_frame = SeparatedFrame(self, "right", 10, 10, left=False)
        
        # 绑定按钮事件
        self.left_frame.add_button.bind("<Button-1>", lambda e: self.add_stats_class())
        self.left_frame.remove_button.bind("<Button-1>", lambda e: self.remove_stats_class())

        self.right_frame.add_button.bind("<Button-1>", lambda e: self.add_stats_item())
        self.right_frame.remove_button.bind("<Button-1>", lambda e: self.remove_stats_item())
        self.right_frame.increment_button.bind("<Button-1>", lambda e: self.increment())
        self.right_frame.decrement_button.bind("<Button-1>", lambda e: self.decrement())
        
        # 绑定列表框的选择事件
        self.left_frame.stats_listbox.bind("<<ListboxSelect>>", lambda e: self.get_selected_stats_class())
        
        # 绑定保存按钮事件
        self.left_frame.save_button.bind("<Button-1>", lambda e: self.save_stats_class())
        
        # 绑定导出按钮事件
        self.left_frame.export_button.bind("<Button-1>", lambda e: self.export_data())
        
        self.load_stats_class()
        
    # 定义从json文件加载统计类的方法
    def load_stats_class(self):
        # 调用json文件处理类的方法，从json文件中加载字典数据
        self.stats_map = self.json_dict_handler.load_dict()
        print(f"从json文件中加载统计类: {self.stats_map}")
        if self.stats_map:
            # 清空列表框
            self.left_frame.stats_listbox.delete(0, tk.END)
            
            # 向列表框中添加统计类
            for stats_class in self.stats_map.keys():
                self.left_frame.stats_listbox.insert(tk.END, stats_class)
        else:
            print("从json文件中加载统计类失败")
    
    # 定义添加统计类的方法
    def add_stats_class(self):
        # 从消息框中获取用户输入
        stats_class = simpledialog.askstring("添加统计类", "请输入统计类名:")
        
        if stats_class:
            self.left_frame.stats_listbox.insert(tk.END, stats_class)
            print(f"添加统计类: {stats_class}")
            
            # 向字典中添加空列表
            self.stats_map[stats_class] = []
            
            self.save_stats_class()
            
        else:
            print("未输入统计类名")
        
    # 定义移除统计类的方法
    def remove_stats_class(self):
        # 获取当前的统计类索引
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            # 从字典中移除对应项
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"移除统计类的字符串: {str}")
            self.stats_map.pop(str, None)
            
            # 移除选中的统计类
            self.left_frame.stats_listbox.delete(selected_index)
            
            # 清空树状视图
            for item in self.right_frame.stats_treeview.get_children():
                self.right_frame.stats_treeview.delete(item)
            
            print(f"移除统计类: {self.left_frame.stats_listbox.get(selected_index)}")
            
            self.save_stats_class()
        else:
            print("未选择任何统计类")
        
        
    # 定义列表框的方法
    def get_selected_stats_class(self):
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"当前选择的统计类: {str}")
            
            # 清空树状视图
            for item in self.right_frame.stats_treeview.get_children():
                self.right_frame.stats_treeview.delete(item)

            # 从字典中获取对应的具体项列表  
            stats_items = self.stats_map.get(str, [])
            print(f"当前选择的统计类的具体项: {stats_items}")
            # 向树状视图中添加具体项
            for item in stats_items:
                self.right_frame.stats_treeview.insert("", tk.END, values=item)
        else:
            print("未选择任何统计类")
            
    
    # 定义保存统计类的方法
    def save_stats_class(self):
        
        # 将字典赋值为新的字典，将次数重置为0
        new_stats_map = {}
        
        # 遍历原始字典中的每个统计类
        for stats_class, stats_items in self.stats_map.items():
            new_stats_map[stats_class] = [[item[0], 0] for item in stats_items]
        
        # 调用json文件处理类的方法，将字典写入json文件
        self.json_dict_handler.save_dict(new_stats_map)
        
    # 定义添加具体项的方法
    def add_stats_item(self):
        # 获取当前选择的统计类
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"当前选择的统计类: {str}")
            
            # 从消息框中获取用户输入
            stats_item = simpledialog.askstring("添加具体项", "请输入具体项名:")
            
            if stats_item:
                # 向map中添加具体项
                self.stats_map.setdefault(str, []).append([stats_item, 0])
                
                # 向树状视图中添加具体项
                self.right_frame.stats_treeview.insert("", tk.END, values=[stats_item, 0])
                print(f"添加具体项: {stats_item}")
            else:
                print("未输入具体项名")
        else:
            print("未选择任何统计类")
            
        
    # 定义移除具体项的方法
    def remove_stats_item(self):
        # 获取当前选择的统计类
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"当前选择的统计类: {str}")
            
            # 获取当前选择的具体项索引
            selected_item_index = self.right_frame.stats_treeview.selection()
            if selected_item_index:
                
                # 从字典中移除对应项
                stats_items = self.stats_map.get(str, [])
                stats_item = self.right_frame.stats_treeview.item(selected_item_index)["text"]
                print(f"当前选择的具体项: {stats_item}")
                if stats_item in stats_items:
                    stats_items.remove(stats_item)
                    
                # 移除选中的具体项
                self.right_frame.stats_treeview.delete(selected_item_index)
                
                print(f"移除具体项: {stats_item}")
            else:
                print("未选择任何具体项")
        else:
            print("未选择任何统计类")
        
    # 定义加1的方法
    def increment(self):
        # 获取当前选择的统计类
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"当前选择的统计类: {str}")
            
            # 获取当前选择的具体项索引
            selected_item_index = self.right_frame.stats_treeview.selection()
            if selected_item_index:
                # 从字典中获取对应项
                stats_items = self.stats_map.get(str, [])
                # 打印从字典中获取的具体项
                # print(f"从字典中获取的具体项: {stats_items}")
                stats_item = self.right_frame.stats_treeview.item(selected_item_index)["values"]
                print(f"当前选择的具体项: {stats_item}")
                if stats_item in stats_items:
                    new = stats_items[stats_items.index(stats_item)]
                    new[1] += 1
                    # 更新树状视图
                    self.right_frame.stats_treeview.item(selected_item_index, values=new)
                
                print(f"加1: {stats_item}")
            else:
                print("未选择任何具体项")
        else:
            print("未选择任何统计类")
        
    # 定义减1的方法
    def decrement(self):
        # 获取当前选择的统计类
        selected_index = self.left_frame.stats_listbox.curselection()
        if selected_index:
            str = self.left_frame.stats_listbox.get(selected_index)
            print(f"当前选择的统计类: {str}")
            
            # 获取当前选择的具体项索引
            selected_item_index = self.right_frame.stats_treeview.selection()
            if selected_item_index:
                # 从字典中获取对应项
                stats_items = self.stats_map.get(str, [])
                # 打印从字典中获取的具体项
                # print(f"从字典中获取的具体项: {stats_items}")
                stats_item = self.right_frame.stats_treeview.item(selected_item_index)["values"]
                print(f"当前选择的具体项: {stats_item}")
                if stats_item in stats_items:
                    new = stats_items[stats_items.index(stats_item)]
                    new[1] -= 1
                    # 更新树状视图
                    self.right_frame.stats_treeview.item(selected_item_index, values=new)
                
                print(f"减1: {stats_item}")
            else:
                print("未选择任何具体项")
        else:
            print("未选择任何统计类")
            
    # 定于导出数据函数
    def export_data(self):
        # 打开文件对话框，选择导出的文件路径
        file_path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV文件", "*.csv")])
        if file_path:
            # 写入CSV文件，使用try-catch处理异常
            try:
                with open(file_path, mode="w", newline="", encoding="utf-8-sig") as file:
                    writer = csv.writer(file)
                    # 写入表头
                    writer.writerow(["统计类", "具体项", "次数"])
                    # 写入数据
                    for stats_class, items in self.stats_map.items():
                        for item, count in items:
                            writer.writerow([stats_class, item, count])
            except Exception as e:
                print(f"导出数据时出错: {e}")
            

if __name__ == "__main__":
    app = SimpleStatsGUI()
    app.mainloop()